package cn.hc.tool.exec.controller;

import cn.hc.tool.common.util.ClassUtil;
import cn.hc.tool.common.util.CollectionUtil;
import cn.hc.tool.common.util.StringUtil;
import cn.hc.tool.exec.aspect.Administrators;
import cn.hc.tool.exec.service.HcBeanService;
import cn.hc.tool.exec.utils.AopTargetUtil;
import cn.hc.tool.exec.utils.HcSpringBeanUtil;
import com.hc.json.adapter.Json;
import com.hc.json.adapter.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author huangchao E-mail:fengquan8866@163.com
 * @version 创建时间：2024/9/24 17:06
 */
@Controller
@RequestMapping("/test")
@Slf4j
@ResponseBody
@Administrators
public class HcToolTestController {

    /**
     * 动态生成接口实现类分隔符
     */
    private static final String SEPARATOR_PROXY_CLASS = "_proxy_";

    @Autowired
    private HcBeanService hcBeanService;

    /**
     * 执行方法
     *
     * @param className  class name
     * @param beanName   bean name
     * @param methodName method name
     */
    @RequestMapping(value = {"/exec", "/exec.html", "/exec.json"})
    public String exec(String className, String beanName, String print, @RequestParam String methodName,
                       @RequestBody String jsonArray) {
        Map<String, Object> r = new HashMap<String, Object>();
        try {
            Object bean = null;
            Method method = null;
            // 判断静态方法
            if (!StringUtil.isBlank(className)) {
                Class<?> c = Class.forName(className);
                Method staticMethod = ClassUtil.getDeclareMethodByName(c, methodName);
                if (staticMethod != null && ClassUtil.isStatic(staticMethod)) {
                    method = staticMethod;
                }
            }
            List<Object> arr = Json.fromJson(jsonArray, new TypeToken<List<Object>>() {
            });
            // 判断动态方法
            if (method == null) {
                bean = hcBeanService.getBean(className, beanName);
                if (bean == null) {
                    r.put("success", false);
                    r.put("message", "consumer不存在");
                    return this.print(r, print);
                }
                // 获取最符合的方法
                Class<?> realClass = this.getRealClass(bean);
                method = hcBeanService.getMethod(realClass.getName(), ClassUtil.getDeclareMethodsByName(realClass, methodName), arr);
                if (method == null) {
                    r.put("success", false);
                    r.put("message", "方法不存在");
                    return this.print(r, print);
                }
                log.info("exec invoke method: {}", ClassUtil.methodStr(method));
            }
            r = this.exec(bean, method, arr);
            log.info("exec调用 {}#{} 出参：{}", className, methodName, print(r, print));
        } catch (InvocationTargetException e) {
            Throwable e2 = e.getTargetException();
            log.error("error InvocationTargetException in exec {}#{}", className, methodName, e2);
            this.fillErrorInfo(r, e2);
        } catch (Exception e) {
            log.error("error in exec {}#{}", className, methodName, e);
            this.fillErrorInfo(r, e);
        }
        return this.print(r, print);
    }

    /**
     * 查询属性
     */
    @RequestMapping(value = {"/field", "/field.html", "/field.json"})
    public String field(String className, String beanName, String print, @RequestParam String fieldName) {
        Map<String, Object> r = new HashMap<String, Object>();
        try {
            Object bean = null;
            Class<?> c = null;
            if (StringUtil.isBlank(className)) {
                bean = HcSpringBeanUtil.getBean(beanName);
                c = getRealClass(bean);
            } else {
                c = Class.forName(className);
            }
            Field f = ClassUtil.getField(c, fieldName);
            Object val = null;
            // 方法存在，且为静态方法
            if (f != null && Modifier.isStatic(f.getModifiers())) {
                val = ClassUtil.get(c, f);
            } else {
                // 查询对应的实例
                if (bean == null) {
                    bean = hcBeanService.getBean(className, beanName);
                }
                if (bean == null) {
                    r.put("success", false);
                    r.put("message", "consumer不存在");
                    return this.print(r, print);
                }
                // 字段不存在，则重新获取字段
                if (f == null) {
                    c = bean.getClass();
                    f = ClassUtil.getField(c, fieldName);
                    if (f == null) {
                        r.put("success", false);
                        r.put("message", "field不存在");
                        return this.print(r, print);
                    }
                }
                if (Modifier.isStatic(f.getModifiers())) {
                    val = ClassUtil.get(c, f);
                } else {
                    val = ClassUtil.getFieldValue(AopTargetUtil.getTarget(bean), fieldName);
                }
            }
            r.put("success", true);
            r.put("data", val);
            log.info("exec调用 {}#{} 属性值：{}", className, fieldName, this.print(r, print));
        } catch (Exception e) {
            log.error("error in field {}#{}", className, fieldName, e);
            r.put("success", false);
            r.put("message", "error msg:" + e.getMessage());
            r.put("errStackTrace", getStackTrace(e));
        }
        return this.print(r, print);
    }

    /**
     * bean转json
     */
    @RequestMapping(value = {"/beanToJson", "/beanToJson.html", "/beanToJson.json"})
    public String beanToJson(String className, String beanName, String print) {
        Map<String, Object> r = new HashMap<String, Object>();
        try {
            Object bean = hcBeanService.getBean(className, beanName);
            r.put("class", bean.getClass().getName());
            r.put("data", this.print(AopTargetUtil.getTarget(bean), print));
        } catch (Exception e) {
            log.error("error in beanToJson {}, {}", className, beanName, e);
            this.fillErrorInfo(r, e);
        }
        return this.print(r, print);
    }

    /**
     * spring beans
     */
    @RequestMapping(value = {"/beans", "/beans.html", "/beans.json"})
    public String beans(String print) {
        Map<String, Object> r = new HashMap<String, Object>();
        try {
            ApplicationContext applicationContext = HcSpringBeanUtil.getApplicationContext();
            r.put("success", true);
            r.put("data", applicationContext.getBeanDefinitionNames());
            log.info("exec调用 属性值：{}", this.print(r, print));
        } catch (Exception e) {
            log.error("error in beans", e);
            r.put("success", false);
            r.put("message", "error msg:" + e.getMessage());
            r.put("errStackTrace", getStackTrace(e));
        }
        return this.print(r, print);
    }

    private String print(Object r, String print) {
        if (Objects.equals(print, "toString")) {
            return r.toString();
        }
        return Json.toJson(r);
    }

    private void fillErrorInfo(Map<String, Object> r, Throwable e2) {
        r.put("success", false);
        r.put("message", "error msg:" + e2.getMessage());
        r.put("errStackTrace", getStackTrace(e2));
    }

    /**
     * 获取真实类（可能是接口）
     *
     * @param proxyClass 代理类对象
     */
    private Class<?> getRealClass(Object proxyClass) {
        Class<?> realClass = ClassUtil.getRealClass(proxyClass.getClass());
        String className = realClass.getName();
        int index = className.indexOf(SEPARATOR_PROXY_CLASS);
        if (index < 0) {
            return realClass;
        }
        try {
            className = className.substring(0, index);
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            log.error("getRealClass, but class is not exist: {}", className, e);
        }
        return realClass;
    }

    /**
     * 获取堆栈字符串
     */
    private static String getStackTrace(Throwable t) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        try {
            t.printStackTrace(pw);
            return sw.toString();
        } catch (Exception e) {
            log.error("print pw error!", e);
        } finally {
            try {
                pw.close();
            } catch (Exception e) {
                log.error("close pw error!", e);
            }
        }
        return null;
    }

    /**
     * 执行方法
     *
     * @param bean   源对象
     * @param method 目标方法
     * @param arr    方法参数
     * @return 方法结果
     * @throws InvocationTargetException 方法执行报错会抛出此异常
     * @throws IllegalAccessException    执行无权限的类、方法引发的异常
     */
    private Map<String, Object> exec(Object bean, Method method, List<Object> arr) throws Exception {
        Map<String, Object> r = new HashMap<String, Object>();
        Class<?>[] cls = method.getParameterTypes();
        Object[] params = new Object[cls.length];
        int i = 0;
        if (!CollectionUtil.isEmpty(arr)) {
            for (Object o : arr) {
                if (o == null) {
                    params[i] = null;
                } else if (method.getGenericParameterTypes()[i] instanceof ParameterizedType) {
                    params[i] = ClassUtil.parseObj(Json.toJson(o), method.getGenericParameterTypes()[i], cls[i]);
                } else if (cls[i].isAssignableFrom(o.getClass())) {
                    params[i] = o;
                } else {
                    params[i] = Json.fromJson(Json.toJson(o), cls[i]);
                }
                i++;
            }
        }
        // 参数不足，多余参数补null
        if (i < params.length) {
            for (; i < params.length; i++) {
                params[i] = null;
            }
        }
        Object result = ClassUtil.invoke(method, ClassUtil.isPrivate(method) ? AopTargetUtil.getTarget(bean) : bean, params);
        r.put("success", true);
        r.put("data", result);
        return r;
    }

}
